////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001-2004.
// -------------------------------------------------------------------------
//  File name:   MaterialNode.cpp
//  Version:     v1.00
//  Created:     11/5/2004 by Timur.
//  Compilers:   Visual Studio.NET 2003
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "MaterialNode.h"
#include "AnimTrack.h"

#include <ISystem.h>
#include <I3DEngine.h>
#include <IRenderer.h>
#include <IShader.h>

#define s_nodeParamsInitialized s_nodeParamsInitializedMat
#define s_nodeParams s_nodeParamsMat
#define AddSupportedParam AddSupportedParamMat

namespace
{
	bool s_nodeParamsInitialized = false;
	std::vector<IAnimNode::SParamInfo> s_nodeParams;

	void AddSupportedParam( const char *sName,int paramId,EAnimValue valueType )
	{
		IAnimNode::SParamInfo param;
		param.name = sName;
		param.paramId = paramId;
		param.valueType = valueType;
		s_nodeParams.push_back( param );
	}
}

enum EMaterialNodeParam
{
	MTL_PARAM_OPACITY    = APARAM_USER + 1,
	MTL_PARAM_AMBIENT    = APARAM_USER + 2,
	MTL_PARAM_DIFFUSE    = APARAM_USER + 3,
	MTL_PARAM_SPECULAR   = APARAM_USER + 4,
	MTL_PARAM_EMMISION   = APARAM_USER + 5,
	MTL_PARAM_SHININESS  = APARAM_USER + 6,
	
	MTL_PARAM_SHADER_PARAM1  = APARAM_USER + 100,
};

//////////////////////////////////////////////////////////////////////////
CAnimMaterialNode::CAnimMaterialNode()
{
	SetFlags( GetFlags()|ANODE_FLAG_CAN_CHANGE_NAME );

	CAnimMaterialNode::Initialize();
}

//////////////////////////////////////////////////////////////////////////
void CAnimMaterialNode::Initialize()
{
	if (!s_nodeParamsInitialized)
	{
		s_nodeParamsInitialized = true;

		s_nodeParams.reserve(15);
		AddSupportedParam( "Opacity",MTL_PARAM_OPACITY,AVALUE_FLOAT );
		AddSupportedParam( "Ambient",MTL_PARAM_AMBIENT,AVALUE_VECTOR );
		AddSupportedParam( "Diffuse",MTL_PARAM_DIFFUSE,AVALUE_VECTOR );
		AddSupportedParam( "Specular",MTL_PARAM_SPECULAR,AVALUE_VECTOR );
		AddSupportedParam( "Emission",MTL_PARAM_EMMISION,AVALUE_VECTOR );
		AddSupportedParam( "Shininess",MTL_PARAM_SHININESS,AVALUE_FLOAT );
		AddSupportedParam( "Shader Param 1",MTL_PARAM_SHADER_PARAM1,AVALUE_FLOAT );
		AddSupportedParam( "Shader Param 2",MTL_PARAM_SHADER_PARAM1+1,AVALUE_FLOAT );
		AddSupportedParam( "Shader Param 3",MTL_PARAM_SHADER_PARAM1+2,AVALUE_FLOAT );
		AddSupportedParam( "Shader Param 4",MTL_PARAM_SHADER_PARAM1+3,AVALUE_FLOAT );
		AddSupportedParam( "Shader Param 5",MTL_PARAM_SHADER_PARAM1+4,AVALUE_FLOAT );
		AddSupportedParam( "Shader Param 6",MTL_PARAM_SHADER_PARAM1+5,AVALUE_FLOAT );
		AddSupportedParam( "Shader Param 7",MTL_PARAM_SHADER_PARAM1+6,AVALUE_FLOAT );
		AddSupportedParam( "Shader Param 8",MTL_PARAM_SHADER_PARAM1+7,AVALUE_FLOAT );
		AddSupportedParam( "Shader Param 9",MTL_PARAM_SHADER_PARAM1+8,AVALUE_FLOAT );
	}
}

//////////////////////////////////////////////////////////////////////////
int CAnimMaterialNode::GetParamCount() const
{
	return s_nodeParams.size();
}

//////////////////////////////////////////////////////////////////////////
bool CAnimMaterialNode::GetParamInfo( int nIndex, SParamInfo &info ) const
{
	if (nIndex >= 0 && nIndex < (int)s_nodeParams.size())
	{
		info = s_nodeParams[nIndex];
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CAnimMaterialNode::GetParamInfoFromId( int paramId, SParamInfo &info ) const	
{
	for (unsigned int i = 0; i < s_nodeParams.size(); i++)
	{
		if (s_nodeParams[i].paramId == paramId)
		{
			info = s_nodeParams[i];
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CAnimMaterialNode::Animate( SAnimContext &ec )
{
	int paramCount = NumTracks();
	if (paramCount <= 0)
		return;

	// Find material.

	IMaterial *pMtl=0;

	const char * pName = GetName();

	const char * pCh;

	if(pCh = strstr(pName, ".["))
	{
		char MatName[64];
		strncpy(MatName, pName, pCh-pName);
		MatName[pCh-pName]=0;
		IMaterial * pMat = gEnv->p3DEngine->GetMaterialManager()->FindMaterial( MatName );
		if(!pMat)
			return;
		pCh+=2;
		if(!(*pCh))
			return;
		int index = atoi(pCh)-1;
		if(index<0 || index>=pMat->GetSubMtlCount())
			return;
		pMtl = pMat->GetSubMtl(index);
	}
	else
	{
		pMtl = gEnv->p3DEngine->GetMaterialManager()->FindMaterial( GetName() );
	}

	if (!pMtl)
		return;

	const char *sStr = GetName();


	const SShaderItem& shaderItem = pMtl->GetShaderItem();
	IRenderShaderResources *pShaderResources = shaderItem.m_pShaderResources;
	if (!pShaderResources)
		return;

	float fValue;
	Vec3 vValue;
	
	int trackCount = NumTracks();
	for (int paramIndex = 0; paramIndex < trackCount; paramIndex++)
	{
		int paramId = m_tracks[paramIndex].paramId;
		IAnimTrack *pTrack = m_tracks[paramIndex].track;

		switch (paramId)
		{
		case MTL_PARAM_OPACITY:
			pTrack->GetValue( ec.time,fValue );
			pShaderResources->GetOpacity() = fValue;
			break;
		case MTL_PARAM_DIFFUSE:
			pTrack->GetValue( ec.time,vValue );
      {
        ColorF& matDiffuse = pShaderResources->GetDiffuseColor();
				matDiffuse.r = vValue.x;
        matDiffuse.g = vValue.y;
        matDiffuse.b = vValue.z;
      }
			break;
		case MTL_PARAM_SPECULAR:
			pTrack->GetValue( ec.time,vValue );
      {
        ColorF& matSpecular = pShaderResources->GetSpecularColor();
				matSpecular.r = vValue.x;
        matSpecular.g = vValue.y;
        matSpecular.b = vValue.z;
      }
			break;
		case MTL_PARAM_EMMISION:
			pTrack->GetValue( ec.time,vValue );
      {
        ColorF& matEmission = pShaderResources->GetEmissiveColor();
				matEmission.r = vValue.x;
        matEmission.g = vValue.y;
        matEmission.b = vValue.z;
      }
			break;
		case MTL_PARAM_SHININESS:
			pTrack->GetValue( ec.time,fValue );
      {
        float& fSpecExp = pShaderResources->GetSpecularShininess();
				fSpecExp = fValue;
      }
			break;
		default:
			if (paramId >= MTL_PARAM_SHADER_PARAM1)
			{
				int id = paramId - MTL_PARAM_SHADER_PARAM1;
				if (id < (int)pShaderResources->GetParameters().size())
				{
					pTrack->GetValue( ec.time,fValue );
					pShaderResources->GetParameters()[id].m_Value.m_Float = fValue;
				}
			}
		}
	}

	IShader* pShader = shaderItem.m_pShader;
	if (pShader)
	{
		shaderItem.m_pShaderResources->UpdateConstants(pShader);
	}
}

#undef s_nodeParamsInitialized
#undef s_nodeParams
#undef AddSupportedParam
